package cn.good.yan.serivce.impl;

import cn.good.yan.apis.AxxFeignApi;
import cn.good.yan.apis.CxxFeignApi;
import cn.good.yan.entities.Bxx;
import cn.good.yan.mapper.BxxMapper;
import cn.good.yan.serivce.BxxService;
import io.seata.core.context.RootContext;
import io.seata.spring.annotation.GlobalTransactional;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * Bxx  - 模拟 - 订单表 （记录每个用户下了订单，买了哪个产品，有几个，花了多少钱）
 * 依次的启动： 2001 、 2002 、 2003
 * 演示前提：
 * 库存表中：1号产品有 100库存 已有0  剩余100
 * 账户表中：1号用户有 100元 已花0  剩余100
 * 创建订单 - 1号用户 买 1好产品 n个数量 n*10 元
 *
 * @author shengren.yan
 * @create 2022-04-06
 */
@Service
@Slf4j
public class BxxServiceImpl implements BxxService {
    @Resource
    private BxxMapper bxxMapper;
    @Resource
    private CxxFeignApi cxxFeignApi; // cxx的远程调用
    @Resource
    private AxxFeignApi axxFeignApi; // axx的远程调用

    // 初始值：仓库：1号产品有 100库存 已有0  剩余100 、账户： 1号用户有 100元 已花0  剩余100
    // 演示1：不加事务注解：
    // 请求：http://localhost:2001/bcc/create/2  服务成功 ok
    //       仓库：1号产品有 100库存 已有2  剩余98 、账户： 1号用户有 100元 已花20  剩余80

    // 演示2：不加事务注解：在2003 服务，增加超时耗时的请求
    // 请求：http://localhost:2001/bcc/create/2  服务成功 ok
    // 导致：仓库：1号产品有 100库存 已有4  剩余96 、账户： 1号用户有 100元 已花20  剩余80  - 会导致 -调用账户 axx服务，超时异常
    // 后面的sql无法执行，出现数据不一致性问题，违背原子性，前面执行成功，后面执行失败
    // 解决：加@GlobalTransactional 全局事务

    // 演示3：加全局事务注解：在2003 服务，增加超时耗时的请求 - 恢复数据
    // 请求：http://localhost:2001/bcc/create/2  服务成功 ok
    // 第一阶段提交：仓库：1号产品有 100库存 已有0  剩余98 、 账户： 1号用户有 100元 已花0  剩余80  - 有个超时等待，导致异常
    // 第二阶段提交：仓库：1号产品有 100库存 已有0  剩余100 、账户： 1号用户有 100元 已花0  剩余100 - 有异常 - 进行回滚

    // 演示4：加全局事务注解：在2003 服务，增加异常代码 - 恢复数据
    // 请求：http://localhost:2001/bcc/create/2  服务成功 ok
    // 第一阶段提交：仓库：1号产品有 100库存 已有0  剩余98 、 账户： 1号用户有 100元 已花0  剩余80  - 有个超时等待，导致异常
    // 第二阶段提交：仓库：1号产品有 100库存 已有0  剩余100 、账户： 1号用户有 100元 已花0  剩余100 - 有异常 - 进行回滚
    @Override
    @GlobalTransactional(name = "bxx-create", rollbackFor = Exception.class) //AT模式
    public void create(Bxx bxx) {
        // xid全局事务id的检查
        String xid = RootContext.getXID();
        // 1 新建订单
        log.info("---- 步骤1 ------ 开始新建订单: " + "\t" + "全局事务: " + xid);
        // 新增订单 - SQL
        int result = bxxMapper.insertSelective(bxx);
        // 成功后的实体
        Bxx fromDB = null;

        if (result > 0) {
            // 从mysql里面查出刚插入的记录
            fromDB = bxxMapper.selectOne(bxx);

            // 2 扣减库存
            log.info("---- 步骤2 ---调用cxx--- 扣减库存: " + "\t" + "数量: " + fromDB.getCount() + " 开始");
            cxxFeignApi.decrease(fromDB.getProductId(), fromDB.getCount());
            log.info("---- 步骤2 ---调用cxx--- 扣减库存: " + "\t" + "数量: " + fromDB.getCount() + " 结束");
            System.out.println();

            // 3 扣减账户余额
            log.info("-------> 订单微服务开始调用Account账号，做扣减money");
            log.info("---- 步骤3 ---调用axx--- 扣减账户余额: " + "\t" + "金额: " + fromDB.getMoney() + " 开始");
            axxFeignApi.decrease(fromDB.getUserId(), fromDB.getMoney());
            log.info("---- 步骤3 ---调用axx--- 扣减账户余额: " + "\t" + "金额: " + fromDB.getMoney() + " 结束");
            System.out.println();

            // 4 修改订单状态 -- 本案例 -- 没有，就更新 -- 用户id 为 10，
//            fromDB.setStatus(1);
            fromDB.setUserId(10l); // 代表状态 0k
            bxxMapper.update(fromDB);
            log.info("---- 步骤4 ---调用bxx--- 更新最新状态");

        }
        System.out.println();
        log.info("---- 步骤1 ------ 结束新建订单: " + "\t" + "全局事务: " + xid);
    }

}
